import { Box3, Color, Object3D, Vector3 } from "three";
import { App } from "../../application";
import Const from "../../application/constants/Const";
import DemTile from "../../map/Tile/DemTile";
import HtmlMarker from "../models/HtmlMarker";
import MeshUtil from "../../renderExtension/utils/MeshUtil";

/**
 * 等高线分析
 */
export default class ContourLineAnalysis {
    private isOpenAnalysis: boolean = false;
    private loadTileEvent;
    private demLayer;
    private data: { isOpenContourLine: boolean, contourLineWidth: number, contourLineColor: Vector3, contourLineInterval: number, model?: Object3D };
    private offsetData: { realHeightStart: number, realHeightEnd: number };
    private marker;
    private monitorHeightEvent;
    private realHeightStart: number = 0;
    private modelHeightStart: number = 0;
    private heightScale: number = 1;

    /**
     * 等高线分析
     * @param data 等高线分析用到的参数
     * @param data.isOpenContourLine 是否开启等高线分析
     * @param data.contourLineWidth 等高线宽度
     * @param data.contourLineColor 等高线颜色
     * @param data.contourLineInterval 等高线间隔
     * @param data.model 这是可选参数，如果不传，等高线分析作用在dem地图上；如果传了，等高线分析作用在传入的模型上
     * @param data.realHeightStart 在现实世界中，等高线分析的起始高度
     * @param data.realHeightEnd 在现实世界中，等高线分析的结束高度
     */
    public constructor(data: { isOpenContourLine: boolean, contourLineWidth: number, contourLineColor: string, contourLineInterval: number, model?: Object3D },
        offsetData?: { realHeightStart: number, realHeightEnd: number }) {
        let color = new Color(data.contourLineColor);
        let colorVector = new Vector3(color.r, color.g, color.b);
        this.data = {
            isOpenContourLine: data.isOpenContourLine, contourLineWidth: data.contourLineWidth, contourLineColor: colorVector,
            contourLineInterval: data.contourLineInterval, model: data.model
        };
        this.offsetData = offsetData;
        this.loadTileEvent = this.loadTileListener.bind(this);
        this.monitorHeightEvent = this.momitorHeightCallback.bind(this);
    }

    public openAnalysis() {
        if (this.isOpenAnalysis) { //等高线分析本来就在开着
            return;
        }

        if (this.offsetData && this.data.model) {
            let modelBox = MeshUtil.computeBox(this.data.model);

            this.realHeightStart = this.offsetData.realHeightStart;
            this.modelHeightStart = modelBox.min.z;
            this.heightScale = (this.offsetData.realHeightEnd - this.offsetData.realHeightStart) / (modelBox.max.z - modelBox.min.z);
        }

        if (this.data.model) { //等高线分析作用在模型上
            let self = this;
            this.data.model.traverse(function (obj) {
                if (obj.type == 'Mesh' && obj['material']) {
                    if (Array.isArray(obj['material'])) {
                        for (let i = 0; i < obj['material'].length; i++) {
                            obj['material'][i].setContourLineData(self.data); //设置等高线数据
                        }
                    }
                    else {
                        obj['material'].setContourLineData(self.data); //设置等高线数据
                    }
                }
            });
            //@ts-ignore
            this.data.model.addEventListener('afterLoadTile', this.loadTileEvent);
        } else { //等高线分析作用在dem底图上
            this.demLayer = this.getDemLayer();
            if (!this.demLayer) { //当前所处的底图图层没有dem数据
                return;
            }
            this.closeAnalysis(); //关闭等高线分析

            let self = this;
            this.demLayer.children.forEach((demTile: DemTile) => {
                demTile.material['setContourLineData'](self.data); //设置等高线数据
            });
            this.demLayer.addEventListener('afterLoadTile', this.loadTileEvent);
        }


        let app = App.getInstance();
        app.mouse.addRaycasterType('MouseMove');  //如果要精确的获取鼠标在地图上的位置，一定要调用这段代码，会对性能有所影响。
        app.on('MouseMove', this.monitorHeightEvent);

        this.marker = this.createHtmlMarker();
        this.marker.visible = true;

        this.isOpenAnalysis = true;
    }

    public closeAnalysis() {
        if (!this.isOpenAnalysis) { //等高线分析本来就没有开着
            return;
        }

        if (this.data.model) {
            let contourLineData = { isOpenContourLine: false };
            this.data.model.traverse(function (obj) {
                if (obj.type == 'Mesh' && obj['material']) {
                    if (Array.isArray(obj['material'])) {
                        for (let i = 0; i < obj['material'].length; i++) {
                            obj['material'][i].setContourLineData(contourLineData); //设置等高线数据
                        }
                    }
                    else {
                        obj['material'].setContourLineData(contourLineData); //设置等高线数据
                    }
                }
            });
        } else {
            if (!this.demLayer) { //当前所处的底图图层没有dem数据
                return;
            }

            this.demLayer.children.forEach((demTile: DemTile) => {
                let contourLineData = { isOpenContourLine: false };
                demTile.material['setContourLineData'](contourLineData); //设置等高线数据
            });

            this.demLayer.removeEventListener('afterLoadTile', this.loadTileEvent);
        }

        App.getInstance().off('MouseMove', this.monitorHeightEvent);
        App.getInstance().remove(this.marker);
        this.isOpenAnalysis = false;
    }

    private getDemLayer() { //当前所在图层是否有dem数据 
        let app = App.getInstance();
        let demLayer = null; //是否包含dem
        for (let i = 0; i < app.options.Map.baseLayer.length; i++) {
            if (app.options.Map.baseLayer[i].tileType == Const.tileType.demType) {
                let type = app.options.Map.baseLayer[i].tileType;
                let layer = app.map.getLayersByType(type)[0]; //dem图层只有一个图层
                demLayer = layer;
                return demLayer;
            }
        }
        return demLayer;
    }

    private loadTileListener(event) {
        let newModel: Object3D = event.message as Object3D;
        let self = this;
        newModel.traverse(function (obj) {
                if (obj.type == 'Mesh' && obj['material']) {
                    if (Array.isArray(obj['material'])) {
                        for (let i = 0; i < obj['material'].length; i++) {
                            obj['material'][i].setContourLineData(self.data); //设置等高线数据
                        }
                    }
                    else {
                        obj['material'].setContourLineData(self.data); //设置等高线数据
                    }
                }
            });
    }

    private mod(x, y) {
        return x - y * Math.floor(x / y);
    }

    private createHtmlMarker() {
        //初始化要显示的标注的html内容
        const div = document.createElement('div');
        div.className = 'label';
        div.style.backgroundColor = 'transparent';
        //初始化标注组件，并将htmlelement作为参数传入，这里是一个div
        let label = new HtmlMarker(div);
        //设置组件在三维场景中的位置
        // let position = point.toEPSGWeb();; //;
        // label.position.set(position.x, position.y, 0);
        //设置对其方式，0,0为中心对齐
        label.center.set(-0.1, 0.5);
        label.openCollisionCheck(); //开启marker的碰撞检测
        //将组件添加至场景中
        App.getInstance().add(label);
        return label;
    }

    private momitorHeightCallback(e) {
        if (this.data.model) {
            if (Array.isArray(e.object['material'])) {
                if (!e.object['material'][0].contourLineData) {
                    this.marker.visible = false;
                    return;
                }
            }
            else {
                if (!e.object['material'].contourLineData) {
                    this.marker.visible = false;
                    return;
                }
            }
        }
        let height = e.lnglat.z;
        height = (height - this.modelHeightStart) * this.heightScale + this.realHeightStart;
        //对height四舍五入保留两位小数
        height = Math.round(height * 100) / 100;

        this.marker.position.copy(e.lnglat.clone().toEPSGWeb());
        this.marker.element.textContent = '高度: ' + height;
        this.marker.visible = true;
    }
}